Convert gtrnctr to Format class (#839)
authortsteven4 <13596209+tsteven4@users.noreply.github.com>
Sat, 29 Jan 2022 20:24:46 +0000 (13:24 -0700)
committerGitHub <noreply@github.com>
Sat, 29 Jan 2022 20:24:46 +0000 (13:24 -0700)
* Convert gtrnctr to Format class

* fix includes

* make ingorelist const

CMakeLists.txt
GPSBabel.pro
gtrnctr.cc
gtrnctr.h [new file with mode: 0644]
vecs.h

index c5539f4a5c1fe4aad6601347f14d151af158bf05..cb48a65fe50d99011619b404d9afcb320855c900 100644 (file)
@@ -240,6 +240,7 @@ set(HEADERS
   globalsat_sport.h
   gpx.h
   grtcirc.h
+  gtrnctr.h
   heightgrid.h
   holux.h
   html.h
index e4ba04ad740fe011c6bc4059b0748eb531636861..9957d9f4fa9ea8609026caa4101d628ca269e580 100644 (file)
@@ -227,6 +227,7 @@ HEADERS =  \
   globalsat_sport.h \
   gpx.h \
   grtcirc.h \
+  gtrnctr.h \
   heightgrid.h \
   holux.h \
   html.h \
index 693199fd1454f20d94da6b6287a2e32c3569006f..bc6aa09347026d6944e5b107f3948385834d2df5 100644 (file)
  * http://www8.garmin.com/xmlschemas/ActivityExtensionv2.xsd
  */
 
-#include <QXmlStreamAttributes>
+#include "gtrnctr.h"
 
-#include "defs.h"
-#include "xmlgeneric.h"
-#include <cstdio>
+#include <QByteArray>            // for QByteArray
+#include <QDateTime>             // for QDateTime
+#include <QtGlobal>              // for qPrintable
+#include <QXmlStreamAttributes>  // for QXmlStreamAttributes
+
+#include <cstdarg>               // for va_end, va_list, va_start
+#include <cstdio>                // for snprintf
+#include <cstdlib>               // for atoi
+#include <iterator>              // for size
+#include <optional>              // for optional
+#include <type_traits>           // for add_const<>::type
+
+#include "defs.h"                // for Waypoint, route_head, computed_trkdata, waypt_add, route_disp, track_disp_all, case_ignore_strncmp, track_add_head, track_add_wpt, track_recompute, xml_parse_time, CSTR, wp_flags, WAYPT_SET, unknown_alt
+#include "xmlgeneric.h"          // for xg_string, build_xg_tag_map, xml_deinit, xml_init, xml_read
 
-static gbfile* ofd;
-static int lap_ct = 0;
-static int lap_s = 0;
-static Waypoint* wpt_tmp;
-static route_head* trk_head;
 
 #define MYNAME "gtc"
 
-#define GTC_MAX_NAME_LEN 15
-
-#define MAX_SPORTS 4
-static char gtc_sportlist[MAX_SPORTS][16] = { "Biking", "Running", "MultiSport", "Other" };
-static int gtc_sport = 0;
-static int gtc_course_flag;
-
-static gpsbabel::DateTime gtc_least_time;
-static gpsbabel::DateTime gtc_most_time;
-static double gtc_start_lat;
-static double gtc_start_long;
-static double gtc_end_lat;
-static double gtc_end_long;
-
-static char* opt_sport, *opt_course;
-
-static
-QVector<arglist_t> gtc_args = {
-  {
-    "course", &opt_course, "Write course rather than history, default yes",
-    "1", ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
-  },
-  {
-    "sport", &opt_sport, "Sport: Biking (deflt), Running, MultiSport, Other",
-    "Biking", ARGTYPE_STRING, ARG_NOMINMAX, nullptr
-  },
-};
-
-/* Tracks */
-static xg_callback     gtc_trk_s;
-static xg_callback     gtc_trk_ident;
-static xg_callback     gtc_trk_lap_s, gtc_trk_lap_e;
-static xg_callback     gtc_trk_pnt_s, gtc_trk_pnt_e;
-static xg_callback     gtc_trk_utc;
-static xg_callback     gtc_trk_lat;
-static xg_callback     gtc_trk_long;
-static xg_callback     gtc_trk_alt;
-static xg_callback     gtc_trk_dist;
-static xg_callback     gtc_trk_hr;
-static xg_callback     gtc_trk_cad;
-static xg_callback     gtc_trk_pwr;
-static xg_callback     gtc_trk_spd;
-static xg_callback     gtc_wpt_crs_s, gtc_wpt_crs_e;
-static xg_callback     gtc_wpt_pnt_s, gtc_wpt_pnt_e;
-static xg_callback     gtc_wpt_ident;
-static xg_callback     gtc_wpt_lat;
-static xg_callback     gtc_wpt_long;
-static xg_callback     gtc_wpt_icon;
-static xg_callback     gtc_wpt_notes;
-
-static xg_tag_mapping gtc_map[] = {
-  /* courses tcx v1 & v2 */
-  { gtc_trk_s,    cb_start, "/Courses/Course" },
-  { gtc_trk_ident,cb_cdata, "/Courses/Course/Name"},
-  { gtc_trk_pnt_s,cb_start, "/Courses/Course/Track/Trackpoint" },
-  { gtc_trk_pnt_e,cb_end,   "/Courses/Course/Track/Trackpoint" },
-  { gtc_trk_utc,  cb_cdata, "/Courses/Course/Track/Trackpoint/Time" },
-  { gtc_trk_lat,  cb_cdata, "/Courses/Course/Track/Trackpoint/Position/LatitudeDegrees" },
-  { gtc_trk_long, cb_cdata, "/Courses/Course/Track/Trackpoint/Position/LongitudeDegrees" },
-  { gtc_trk_alt,  cb_cdata, "/Courses/Course/Track/Trackpoint/AltitudeMeters" },
-  { gtc_trk_hr,   cb_cdata, "/Courses/Course/Track/Trackpoint/HeartRateBpm" },
-  { gtc_trk_cad,  cb_cdata, "/Courses/Course/Track/Trackpoint/Cadence" },
-  { gtc_wpt_crs_s,cb_start, "/Courses/Course/CoursePoint" },
-  { gtc_wpt_crs_e,cb_end,   "/Courses/Course/CoursePoint" },
-  { gtc_wpt_ident,cb_cdata, "/Courses/Course/CoursePoint/Name"},
-  { gtc_trk_utc,  cb_cdata, "/Courses/Course/CoursePoint/Time"},
-  { gtc_wpt_lat,  cb_cdata, "/Courses/Course/CoursePoint/Position/LatitudeDegrees"},
-  { gtc_wpt_long, cb_cdata, "/Courses/Course/CoursePoint/Position/LongitudeDegrees"},
-  { gtc_trk_alt,  cb_cdata, "/Courses/Course/CoursePoint/AltitudeMeters" },
-  { gtc_wpt_icon, cb_cdata, "/Courses/Course/CoursePoint/PointType" },
-  { gtc_wpt_notes,cb_cdata, "/Courses/Course/CoursePoint/Notes" },
-
-  /* history tcx v2 (activities) */
-  { gtc_trk_s,    cb_start, "/Activities/Activity" },
-  { gtc_trk_ident,cb_cdata, "/Activities/Activity/Id" },
-  { gtc_trk_lap_s,cb_start, "/Activities/Activity/Lap" },
-  { gtc_trk_lap_e,cb_end,   "/Activities/Activity/Lap" },
-  { gtc_trk_pnt_s,cb_start, "/Activities/Activity/Lap/Track/Trackpoint" },
-  { gtc_trk_pnt_e,cb_end,   "/Activities/Activity/Lap/Track/Trackpoint" },
-  { gtc_trk_utc,  cb_cdata, "/Activities/Activity/Lap/Track/Trackpoint/Time" },
-  { gtc_trk_lat,  cb_cdata, "/Activities/Activity/Lap/Track/Trackpoint/Position/LatitudeDegrees" },
-  { gtc_trk_long, cb_cdata, "/Activities/Activity/Lap/Track/Trackpoint/Position/LongitudeDegrees" },
-  { gtc_trk_alt,  cb_cdata, "/Activities/Activity/Lap/Track/Trackpoint/AltitudeMeters" },
-  { gtc_trk_dist, cb_cdata, "/Activities/Activity/Lap/Track/Trackpoint/DistanceMeters" },
-  { gtc_trk_hr,   cb_cdata, "/Activities/Activity/Lap/Track/Trackpoint/HeartRateBpm" },
-  { gtc_trk_cad,  cb_cdata, "/Activities/Activity/Lap/Track/Trackpoint/Cadence" },
-  { gtc_trk_pwr,  cb_cdata, "/Activities/Activity/Lap/Track/Trackpoint/Extensions/ns3:TPX/ns3:Watts" },
-  // Sample from Marcelo Kittlein 5/2014 declares a default namespace with the start tag of the TPX element,
-  // and thus doesn't use prefixes.
-  { gtc_trk_pwr,  cb_cdata, "/Activities/Activity/Lap/Track/Trackpoint/Extensions/TPX/Watts" },
-  // It looks like Speed and Watts should be siblings, but Garmin can't get
-  // their namespace act very consistent.  This works for a sample provided
-  // by Laurent Desmons in 5/2013.
-  { gtc_trk_spd,  cb_cdata, "/Activities/Activity/Lap/Track/Trackpoint/Extensions/TPX/Speed" },
-
-  /* history tcx v1 */
-  { gtc_trk_s,    cb_start, "/History/Run" },
-  { gtc_trk_ident,cb_cdata, "/History/Run/Id" },
-  { gtc_trk_lap_s,cb_start, "/History/Run/Lap" },
-  { gtc_trk_lap_e,cb_end,   "/History/Run/Lap" },
-  { gtc_trk_pnt_s,cb_start, "/History/Run/Lap/Track/Trackpoint" },
-  { gtc_trk_pnt_e,cb_end,   "/History/Run/Lap/Track/Trackpoint" },
-  { gtc_trk_utc,  cb_cdata, "/History/Run/Lap/Track/Trackpoint/Time" },
-  { gtc_trk_lat,  cb_cdata, "/History/Run/Lap/Track/Trackpoint/Position/LatitudeDegrees" },
-  { gtc_trk_long, cb_cdata, "/History/Run/Lap/Track/Trackpoint/Position/LongitudeDegrees" },
-  { gtc_trk_alt,  cb_cdata, "/History/Run/Lap/Track/Trackpoint/AltitudeMeters" },
-  { gtc_trk_hr,   cb_cdata, "/History/Run/Lap/Track/Trackpoint/HeartRateBpm" },
-  { gtc_trk_cad,  cb_cdata, "/History/Run/Lap/Track/Trackpoint/Cadence" },
-
-  { gtc_wpt_pnt_s,cb_start, "/Courses/Course/Lap/BeginPosition" },
-  { gtc_wpt_pnt_e,cb_end, "/Courses/Course/Lap/BeginPosition" },
-  { gtc_wpt_lat,  cb_cdata, "/Courses/Course/Lap/BeginPosition/LatitudeDegrees" },
-  { gtc_wpt_long, cb_cdata, "/Courses/Course/Lap/BeginPosition/LongitudeDegrees" },
-  { gtc_trk_alt,  cb_cdata, "/Courses/Course/Lap/BeginAltitudeMeters" },
-
-  { nullptr,   (xg_cb_type)0,         nullptr}
-};
-
-static const char*
-gtc_tags_to_ignore[] = {
-  "TrainingCenterDatabase",
-  "CourseFolder",
-  "Running",
-  "Biking",
-  "Other",
-  "Multisport",
-  nullptr,
-};
-
-static void
-gtc_rd_init(const QString& fname)
-{
-  xml_init(fname, gtc_map, nullptr, gtc_tags_to_ignore);
-}
-
-static void
-gtc_read()
+void
+GtrnctrFormat::rd_init(const QString& fname)
+{
+  xml_init(fname, build_xg_tag_map(this, gtc_map), nullptr, gtc_tags_to_ignore, nullptr, true);
+}
+
+void
+GtrnctrFormat::read()
 {
   xml_read();
 }
 
-static void
-gtc_rd_deinit()
+void
+GtrnctrFormat::rd_deinit()
 {
   xml_deinit();
 }
 
-static void
-gtc_wr_init(const QString& fname)
+void
+GtrnctrFormat::wr_init(const QString& fname)
 {
   ofd = gbfopen(fname, "w", MYNAME);
 
   if (opt_sport) {
-    for (int i = 0; i < MAX_SPORTS; i++) {
+    for (unsigned int i = 0; i < std::size(gtc_sportlist); i++) {
       if (0 == case_ignore_strncmp(opt_sport, gtc_sportlist[i], 2)) {
         gtc_sport = i;
         break;
@@ -201,16 +78,14 @@ gtc_wr_init(const QString& fname)
   gtc_course_flag = atoi(opt_course);
 }
 
-static void
-gtc_wr_deinit()
+void
+GtrnctrFormat::wr_deinit()
 {
   gbfclose(ofd);
 }
 
-static int gtc_indent_level;
-
-static void
-gtc_write_xml(int indent, const char* fmt, ...)
+void
+GtrnctrFormat::gtc_write_xml(int indent, const char* fmt, ...)
 {
   va_list args;
 
@@ -230,8 +105,8 @@ gtc_write_xml(int indent, const char* fmt, ...)
   va_end(args);
 }
 
-static void
-gtc_write_xml(int indent, const QString& s)
+void
+GtrnctrFormat::gtc_write_xml(int indent, const QString& s)
 {
   if (indent < 0) {
     gtc_indent_level--;
@@ -245,21 +120,21 @@ gtc_write_xml(int indent, const QString& s)
 
 }
 
-static void
-gtc_lap_start(const route_head*)
+void
+GtrnctrFormat::gtc_lap_start(const route_head* /*unused*/)
 {
   gtc_least_time = gpsbabel::DateTime();
   gtc_most_time = gpsbabel::DateTime();
 }
 
-static computed_trkdata
-gtc_new_study_lap(const route_head* rte)
+computed_trkdata
+GtrnctrFormat::gtc_new_study_lap(const route_head* rte)
 {
   return track_recompute(rte);
 }
 
-static void
-gtc_study_lap(const Waypoint* wpt)
+void
+GtrnctrFormat::gtc_study_lap(const Waypoint* wpt)
 {
   if (wpt->creation_time.isValid() && (!gtc_least_time.isValid())) {
     gtc_least_time = wpt->GetCreationTime();
@@ -279,8 +154,8 @@ gtc_study_lap(const Waypoint* wpt)
   }
 }
 
-static void
-gtc_waypt_pr(const Waypoint* wpt)
+void
+GtrnctrFormat::gtc_waypt_pr(const Waypoint* wpt)
 {
   if (wpt->wpt_flags.is_split != 0) {
     gtc_write_xml(1, "<Trackpoint split=\"yes\">\n");
@@ -335,8 +210,8 @@ gtc_waypt_pr(const Waypoint* wpt)
   gtc_write_xml(-1, "</Trackpoint>\n");
 }
 
-static void
-gtc_fake_hdr(const computed_trkdata& tdata)
+void
+GtrnctrFormat::gtc_fake_hdr(const computed_trkdata& tdata)
 {
   /* handle the CourseLap_t or the ActivityLap_t types. */
   /* note that the elements must appear in the order required by the schema. */
@@ -387,13 +262,16 @@ gtc_fake_hdr(const computed_trkdata& tdata)
 
 }
 
-static void
-gtc_act_hdr(const route_head* rte)
+void
+GtrnctrFormat::gtc_act_hdr(const route_head* rte)
 {
   gtc_write_xml(1, "<Activity Sport=\"%s\">\n", gtc_sportlist[gtc_sport]);
   gtc_lap_start(nullptr);
   computed_trkdata tdata = gtc_new_study_lap(rte);
-  route_disp(rte, gtc_study_lap);
+  auto gtc_study_lap_lambda = [this](const Waypoint* waypointp)->void {
+    gtc_study_lap(waypointp);
+  };
+  route_disp(rte, gtc_study_lap_lambda);
   if (gtc_least_time.isValid()) {
     gtc_write_xml(0, "<Id>%s</Id>\n",
                   CSTR(gtc_least_time.toPrettyString()));
@@ -406,25 +284,28 @@ gtc_act_hdr(const route_head* rte)
   gtc_write_xml(1,"<Track>\n");
 }
 
-static void
-gtc_act_ftr(const route_head*)
+void
+GtrnctrFormat::gtc_act_ftr(const route_head* /*unused*/)
 {
   gtc_write_xml(-1, "</Track>\n");
   gtc_write_xml(-1, "</Lap>\n");
   gtc_write_xml(-1, "</Activity>\n");
 }
 
-static void
-gtc_crs_hdr(const route_head* rte)
+void
+GtrnctrFormat::gtc_crs_hdr(const route_head* rte)
 {
 
   gtc_write_xml(1, "<Course>\n");
   gtc_lap_start(nullptr);
   computed_trkdata tdata = gtc_new_study_lap(rte);
-  route_disp(rte, gtc_study_lap);
+  auto gtc_study_lap_lambda = [this](const Waypoint* waypointp)->void {
+    gtc_study_lap(waypointp);
+  };
+  route_disp(rte, gtc_study_lap_lambda);
 
   if (!rte->rte_name.isEmpty()) {
-    QString name = rte->rte_name.left(GTC_MAX_NAME_LEN);
+    QString name = rte->rte_name.left(kGtcMaxNameLen);
     gtc_write_xml(0, QString("<Name>%1</Name>\n").arg(name));
   } else {
     gtc_write_xml(0, "<Name>New Course</Name>\n");
@@ -436,27 +317,42 @@ gtc_crs_hdr(const route_head* rte)
   gtc_write_xml(1,"<Track>\n");
 }
 
-static void
-gtc_crs_ftr(const route_head*)
+void
+GtrnctrFormat::gtc_crs_ftr(const route_head* /*unused*/)
 {
   gtc_write_xml(-1,"</Track>\n");
   gtc_write_xml(-1, "</Course>\n");
 
 }
 
-static void
-gtc_write()
+void
+GtrnctrFormat::write()
 {
   gtc_write_xml(0, "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n");
   gtc_write_xml(1, "<TrainingCenterDatabase xmlns=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd\">\n");
 
+  auto gtc_waypt_pr_lambda = [this](const Waypoint* waypointp)->void {
+    gtc_waypt_pr(waypointp);
+  };
   if (gtc_course_flag) {
     gtc_write_xml(1, "<Courses>\n");
-    track_disp_all(gtc_crs_hdr, gtc_crs_ftr, gtc_waypt_pr);
+    auto gtc_crs_hdr_lambda = [this](const route_head* rte)->void {
+      gtc_crs_hdr(rte);
+    };
+    auto gtc_crs_ftr_lambda = [this](const route_head* rte)->void {
+      gtc_crs_ftr(rte);
+    };
+    track_disp_all(gtc_crs_hdr_lambda, gtc_crs_ftr_lambda, gtc_waypt_pr_lambda);
     gtc_write_xml(-1, "</Courses>\n");
   } else {
     gtc_write_xml(1, "<Activities>\n");
-    track_disp_all(gtc_act_hdr, gtc_act_ftr, gtc_waypt_pr);
+    auto gtc_act_hdr_lambda = [this](const route_head* rte)->void {
+      gtc_act_hdr(rte);
+    };
+    auto gtc_act_ftr_lambda = [this](const route_head* rte)->void {
+      gtc_act_ftr(rte);
+    };
+    track_disp_all(gtc_act_hdr_lambda, gtc_act_ftr_lambda, gtc_waypt_pr_lambda);
     gtc_write_xml(-1, "</Activities>\n");
   }
 
@@ -464,39 +360,39 @@ gtc_write()
 }
 
 void
-gtc_trk_s(xg_string, const QXmlStreamAttributes*)
+GtrnctrFormat::gtc_trk_s(xg_string /*unused*/, const QXmlStreamAttributes* /*unused*/)
 {
   trk_head = new route_head;
   track_add_head(trk_head);
 }
 
 void
-gtc_trk_ident(xg_string args, const QXmlStreamAttributes*)
+GtrnctrFormat::gtc_trk_ident(xg_string args, const QXmlStreamAttributes* /*unused*/)
 {
   trk_head->rte_name = args;
 }
 
 void
-gtc_trk_lap_s(xg_string, const QXmlStreamAttributes*)
+GtrnctrFormat::gtc_trk_lap_s(xg_string /*unused*/, const QXmlStreamAttributes* /*unused*/)
 {
   lap_ct++;
   lap_s = 1;
 }
 
 void
-gtc_trk_lap_e(xg_string, const QXmlStreamAttributes*)
+GtrnctrFormat::gtc_trk_lap_e(xg_string /*unused*/, const QXmlStreamAttributes* /*unused*/)
 {
   lap_s = 0;
 }
 
 void
-gtc_trk_pnt_s(xg_string, const QXmlStreamAttributes*)
+GtrnctrFormat::gtc_trk_pnt_s(xg_string /*unused*/, const QXmlStreamAttributes* /*unused*/)
 {
   wpt_tmp = new Waypoint;
 }
 
 void
-gtc_trk_pnt_e(xg_string, const QXmlStreamAttributes*)
+GtrnctrFormat::gtc_trk_pnt_e(xg_string /*unused*/, const QXmlStreamAttributes* /*unused*/)
 {
   if (wpt_tmp->longitude != 0. && wpt_tmp->latitude != 0.) {
     if (lap_s) {
@@ -519,62 +415,62 @@ gtc_trk_pnt_e(xg_string, const QXmlStreamAttributes*)
 }
 
 void
-gtc_trk_utc(xg_string args, const QXmlStreamAttributes*)
+GtrnctrFormat::gtc_trk_utc(xg_string args, const QXmlStreamAttributes* /*unused*/)
 {
   wpt_tmp->creation_time = xml_parse_time(args);
 }
 
 void
-gtc_trk_lat(xg_string args, const QXmlStreamAttributes*)
+GtrnctrFormat::gtc_trk_lat(xg_string args, const QXmlStreamAttributes* /*unused*/)
 {
   wpt_tmp->latitude = args.toDouble();
 }
 
 void
-gtc_trk_long(xg_string args, const QXmlStreamAttributes*)
+GtrnctrFormat::gtc_trk_long(xg_string args, const QXmlStreamAttributes* /*unused*/)
 {
   wpt_tmp->longitude = args.toDouble();
 }
 
 void
-gtc_trk_alt(xg_string args, const QXmlStreamAttributes*)
+GtrnctrFormat::gtc_trk_alt(xg_string args, const QXmlStreamAttributes* /*unused*/)
 {
   wpt_tmp->altitude = args.toDouble();
 }
 
-void gtc_trk_dist(const QString& args, const QXmlStreamAttributes*)
+void GtrnctrFormat::gtc_trk_dist(const QString& args, const QXmlStreamAttributes* /*unused*/)
 {
   wpt_tmp->odometer_distance = args.toDouble();
 }
-void gtc_trk_hr(const QString& args, const QXmlStreamAttributes*)
+void GtrnctrFormat::gtc_trk_hr(const QString& args, const QXmlStreamAttributes* /*unused*/)
 {
   wpt_tmp->heartrate = args.toDouble();
 }
-void gtc_trk_cad(const QString& args, const QXmlStreamAttributes*)
+void GtrnctrFormat::gtc_trk_cad(const QString& args, const QXmlStreamAttributes* /*unused*/)
 {
   wpt_tmp->cadence = args.toDouble();
 }
 
 void
-gtc_trk_pwr(xg_string args, const QXmlStreamAttributes*)
+GtrnctrFormat::gtc_trk_pwr(xg_string args, const QXmlStreamAttributes* /*unused*/)
 {
   wpt_tmp->power = args.toDouble();
 }
 
 void
-gtc_trk_spd(xg_string args, const QXmlStreamAttributes*)
+GtrnctrFormat::gtc_trk_spd(xg_string args, const QXmlStreamAttributes* /*unused*/)
 {
   WAYPT_SET(wpt_tmp, speed, args.toDouble());
 }
 
 void
-gtc_wpt_crs_s(const QString&, const QXmlStreamAttributes*)
+GtrnctrFormat::gtc_wpt_crs_s(const QString& /*unused*/, const QXmlStreamAttributes* /*unused*/)
 {
   wpt_tmp = new Waypoint;
 }
 
 void
-gtc_wpt_crs_e(xg_string, const QXmlStreamAttributes*)
+GtrnctrFormat::gtc_wpt_crs_e(xg_string /*unused*/, const QXmlStreamAttributes* /*unused*/)
 {
   if (wpt_tmp->longitude != 0. && wpt_tmp->latitude != 0.) {
     waypt_add(wpt_tmp);
@@ -586,14 +482,14 @@ gtc_wpt_crs_e(xg_string, const QXmlStreamAttributes*)
 }
 
 void
-gtc_wpt_pnt_s(xg_string, const QXmlStreamAttributes*)
+GtrnctrFormat::gtc_wpt_pnt_s(xg_string /*unused*/, const QXmlStreamAttributes* /*unused*/)
 {
   wpt_tmp = new Waypoint;
   lap_ct++;
 }
 
 void
-gtc_wpt_pnt_e(xg_string, const QXmlStreamAttributes*)
+GtrnctrFormat::gtc_wpt_pnt_e(xg_string /*unused*/, const QXmlStreamAttributes* /*unused*/)
 {
   if (wpt_tmp->longitude != 0. && wpt_tmp->latitude != 0.) {
     /* Add the begin position of a CourseLap as
@@ -607,45 +503,25 @@ gtc_wpt_pnt_e(xg_string, const QXmlStreamAttributes*)
   wpt_tmp = nullptr;
 }
 
-void gtc_wpt_ident(const QString& args, const QXmlStreamAttributes*)
+void GtrnctrFormat::gtc_wpt_ident(const QString& args, const QXmlStreamAttributes* /*unused*/)
 {
   wpt_tmp->shortname = (args);
   /* Set also as notes for compatibility with garmin usb format */
   wpt_tmp->notes = (args);
 }
-void gtc_wpt_lat(const QString& args, const QXmlStreamAttributes*)
+void GtrnctrFormat::gtc_wpt_lat(const QString& args, const QXmlStreamAttributes* /*unused*/)
 {
   wpt_tmp->latitude = args.toDouble();
 }
-void gtc_wpt_long(const QString& args, const QXmlStreamAttributes*)
+void GtrnctrFormat::gtc_wpt_long(const QString& args, const QXmlStreamAttributes* /*unused*/)
 {
   wpt_tmp->longitude = args.toDouble();
 }
-void gtc_wpt_icon(const QString& args, const QXmlStreamAttributes*)
+void GtrnctrFormat::gtc_wpt_icon(const QString& args, const QXmlStreamAttributes* /*unused*/)
 {
   wpt_tmp->icon_descr = args;
 }
-void gtc_wpt_notes(const QString& args, const QXmlStreamAttributes*)
+void GtrnctrFormat::gtc_wpt_notes(const QString& args, const QXmlStreamAttributes* /*unused*/)
 {
   wpt_tmp->description = args;
 }
-
-ff_vecs_t gtc_vecs = {
-  ff_type_file,
-  {
-    ff_cap_read                        /* waypoints */,
-    (ff_cap)(ff_cap_read | ff_cap_write)       /* tracks */,
-    ff_cap_none                        /* routes */
-  },
-  gtc_rd_init,
-  gtc_wr_init,
-  gtc_rd_deinit,
-  gtc_wr_deinit,
-  gtc_read,
-  gtc_write,
-  nullptr,
-  &gtc_args,
-  CET_CHARSET_ASCII, 0 /* CET-REVIEW */
-  , NULL_POS_OPS,
-  nullptr
-};
diff --git a/gtrnctr.h b/gtrnctr.h
new file mode 100644 (file)
index 0000000..1971e7a
--- /dev/null
+++ b/gtrnctr.h
@@ -0,0 +1,235 @@
+/*
+    Access Garmin Training Center (Forerunner/Foretracker/Edge) data files.
+
+    Copyright (C) 2006, 2007 Robert Lipe, robertlipe+source@gpsbabel.org
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+ */
+/*
+ * Relevant schema definitions can be found at
+ * http://www8.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd
+ * http://www8.garmin.com/xmlschemas/ActivityExtensionv2.xsd
+ */
+#ifndef GTRNCTR_H_INCLUDED_
+#define GTRNCTR_H_INCLUDED_
+
+#include <QList>                 // for QList
+#include <QString>               // for QString
+#include <QVector>               // for QVector
+#include <QXmlStreamAttributes>  // for QXmlStreamAttributes
+
+#include "defs.h"                // for arglist_t, ff_cap, route_head, Waypoint, computed_trkdata, ARG_NOMINMAX, ff_cap_read, ARGTYPE_BOOL, ARGTYPE_STRING, CET_CHARSET_ASCII, ff_cap_none, ff_cap_write, ff_type, ff_type_file
+#include "format.h"              // for Format
+#include "gbfile.h"              // for gbfile
+#include "src/core/datetime.h"   // for DateTime
+#include "xmlgeneric.h"          // for cb_cdata, xg_functor_map_entry, xg_string, cb_start, cb_end
+
+
+class GtrnctrFormat : public Format
+{
+public:
+  QVector<arglist_t>* get_args() override
+  {
+    return &gtc_args;
+  }
+
+  ff_type get_type() const override
+  {
+    return ff_type_file;
+  }
+
+  QVector<ff_cap> get_cap() const override
+  {
+    return {
+      ff_cap_read                      /* waypoints */,
+      (ff_cap)(ff_cap_read | ff_cap_write)     /* tracks */,
+      ff_cap_none                      /* routes */
+    };
+  }
+
+  QString get_encode() const override
+  {
+    return CET_CHARSET_ASCII;
+  }
+
+  int get_fixed_encode() const override
+  {
+    return 0;
+  }
+
+  void rd_init(const QString& fname) override;
+  void read() override;
+  void rd_deinit() override;
+  void wr_init(const QString& fname) override;
+  void write() override;
+  void wr_deinit() override;
+
+private:
+  /* Constants */
+
+  static constexpr int kGtcMaxNameLen = 15;
+  static constexpr const char* gtc_sportlist[] = { "Biking", "Running", "MultiSport", "Other" };
+
+  static constexpr const char* gtc_tags_to_ignore[] = {
+    "TrainingCenterDatabase",
+    "CourseFolder",
+    "Running",
+    "Biking",
+    "Other",
+    "Multisport",
+    nullptr,
+  };
+
+  /* Member Functions */
+
+  void gtc_write_xml(int indent, const char* fmt, ...);
+  void gtc_write_xml(int indent, const QString& s);
+  void gtc_lap_start(const route_head*  /* unused */);
+  static computed_trkdata gtc_new_study_lap(const route_head* rte);
+  void gtc_study_lap(const Waypoint* wpt);
+  void gtc_waypt_pr(const Waypoint* wpt);
+  void gtc_fake_hdr(const computed_trkdata& tdata);
+  void gtc_act_hdr(const route_head* rte);
+  void gtc_act_ftr(const route_head*  /* unused */);
+  void gtc_crs_hdr(const route_head* rte);
+  void gtc_crs_ftr(const route_head*  /* unused */);
+
+  void gtc_trk_s(xg_string  /* unused */, const QXmlStreamAttributes*  /* unused */);
+  void gtc_trk_ident(xg_string args, const QXmlStreamAttributes*  /* unused */);
+  void gtc_trk_lap_s(xg_string  /* unused */, const QXmlStreamAttributes*  /* unused */);
+  void gtc_trk_lap_e(xg_string  /* unused */, const QXmlStreamAttributes*  /* unused */);
+  void gtc_trk_pnt_s(xg_string  /* unused */, const QXmlStreamAttributes*  /* unused */);
+  void gtc_trk_pnt_e(xg_string  /* unused */, const QXmlStreamAttributes*  /* unused */);
+  void gtc_trk_utc(xg_string args, const QXmlStreamAttributes*  /* unused */);
+  void gtc_trk_lat(xg_string args, const QXmlStreamAttributes*  /* unused */);
+  void gtc_trk_long(xg_string args, const QXmlStreamAttributes*  /* unused */);
+  void gtc_trk_alt(xg_string args, const QXmlStreamAttributes*  /* unused */);
+  void gtc_trk_dist(const QString& args, const QXmlStreamAttributes*  /* unused */);
+  void gtc_trk_hr(const QString& args, const QXmlStreamAttributes*  /* unused */);
+  void gtc_trk_cad(const QString& args, const QXmlStreamAttributes*  /* unused */);
+  void gtc_trk_pwr(xg_string args, const QXmlStreamAttributes*  /* unused */);
+  void gtc_trk_spd(xg_string args, const QXmlStreamAttributes*  /* unused */);
+  void gtc_wpt_crs_s(const QString&  /* unused */, const QXmlStreamAttributes*  /* unused */);
+  void gtc_wpt_crs_e(xg_string  /* unused */, const QXmlStreamAttributes*  /* unused */);
+  void gtc_wpt_pnt_s(xg_string  /* unused */, const QXmlStreamAttributes*  /* unused */);
+  void gtc_wpt_pnt_e(xg_string  /* unused */, const QXmlStreamAttributes*  /* unused */);
+  void gtc_wpt_ident(const QString& args, const QXmlStreamAttributes*  /* unused */);
+  void gtc_wpt_lat(const QString& args, const QXmlStreamAttributes*  /* unused */);
+  void gtc_wpt_long(const QString& args, const QXmlStreamAttributes*  /* unused */);
+  void gtc_wpt_icon(const QString& args, const QXmlStreamAttributes*  /* unused */);
+  void gtc_wpt_notes(const QString& args, const QXmlStreamAttributes*  /* unused */);
+
+  /* Data Members */
+
+  gbfile* ofd{};
+  int lap_ct = 0;
+  int lap_s = 0;
+  Waypoint* wpt_tmp{};
+  route_head* trk_head{};
+
+  unsigned int gtc_sport = 0;
+  int gtc_course_flag{};
+
+  gpsbabel::DateTime gtc_least_time;
+  gpsbabel::DateTime gtc_most_time;
+  double gtc_start_lat{};
+  double gtc_start_long{};
+  double gtc_end_lat{};
+  double gtc_end_long{};
+
+  char* opt_sport{};
+  char* opt_course{};
+
+  QVector<arglist_t> gtc_args = {
+    {
+      "course", &opt_course, "Write course rather than history, default yes",
+      "1", ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
+    },
+    {
+      "sport", &opt_sport, "Sport: Biking (deflt), Running, MultiSport, Other",
+      "Biking", ARGTYPE_STRING, ARG_NOMINMAX, nullptr
+    },
+  };
+
+  QList<xg_functor_map_entry<GtrnctrFormat>> gtc_map = {
+    /* courses tcx v1 & v2 */
+    { &GtrnctrFormat::gtc_trk_s,    cb_start, "/Courses/Course" },
+    { &GtrnctrFormat::gtc_trk_ident,cb_cdata, "/Courses/Course/Name"},
+    { &GtrnctrFormat::gtc_trk_pnt_s,cb_start, "/Courses/Course/Track/Trackpoint" },
+    { &GtrnctrFormat::gtc_trk_pnt_e,cb_end,   "/Courses/Course/Track/Trackpoint" },
+    { &GtrnctrFormat::gtc_trk_utc,  cb_cdata, "/Courses/Course/Track/Trackpoint/Time" },
+    { &GtrnctrFormat::gtc_trk_lat,  cb_cdata, "/Courses/Course/Track/Trackpoint/Position/LatitudeDegrees" },
+    { &GtrnctrFormat::gtc_trk_long, cb_cdata, "/Courses/Course/Track/Trackpoint/Position/LongitudeDegrees" },
+    { &GtrnctrFormat::gtc_trk_alt,  cb_cdata, "/Courses/Course/Track/Trackpoint/AltitudeMeters" },
+    { &GtrnctrFormat::gtc_trk_hr,   cb_cdata, "/Courses/Course/Track/Trackpoint/HeartRateBpm" },
+    { &GtrnctrFormat::gtc_trk_cad,  cb_cdata, "/Courses/Course/Track/Trackpoint/Cadence" },
+    { &GtrnctrFormat::gtc_wpt_crs_s,cb_start, "/Courses/Course/CoursePoint" },
+    { &GtrnctrFormat::gtc_wpt_crs_e,cb_end,   "/Courses/Course/CoursePoint" },
+    { &GtrnctrFormat::gtc_wpt_ident,cb_cdata, "/Courses/Course/CoursePoint/Name"},
+    { &GtrnctrFormat::gtc_trk_utc,  cb_cdata, "/Courses/Course/CoursePoint/Time"},
+    { &GtrnctrFormat::gtc_wpt_lat,  cb_cdata, "/Courses/Course/CoursePoint/Position/LatitudeDegrees"},
+    { &GtrnctrFormat::gtc_wpt_long, cb_cdata, "/Courses/Course/CoursePoint/Position/LongitudeDegrees"},
+    { &GtrnctrFormat::gtc_trk_alt,  cb_cdata, "/Courses/Course/CoursePoint/AltitudeMeters" },
+    { &GtrnctrFormat::gtc_wpt_icon, cb_cdata, "/Courses/Course/CoursePoint/PointType" },
+    { &GtrnctrFormat::gtc_wpt_notes,cb_cdata, "/Courses/Course/CoursePoint/Notes" },
+
+    /* history tcx v2 (activities) */
+    { &GtrnctrFormat::gtc_trk_s,    cb_start, "/Activities/Activity" },
+    { &GtrnctrFormat::gtc_trk_ident,cb_cdata, "/Activities/Activity/Id" },
+    { &GtrnctrFormat::gtc_trk_lap_s,cb_start, "/Activities/Activity/Lap" },
+    { &GtrnctrFormat::gtc_trk_lap_e,cb_end,   "/Activities/Activity/Lap" },
+    { &GtrnctrFormat::gtc_trk_pnt_s,cb_start, "/Activities/Activity/Lap/Track/Trackpoint" },
+    { &GtrnctrFormat::gtc_trk_pnt_e,cb_end,   "/Activities/Activity/Lap/Track/Trackpoint" },
+    { &GtrnctrFormat::gtc_trk_utc,  cb_cdata, "/Activities/Activity/Lap/Track/Trackpoint/Time" },
+    { &GtrnctrFormat::gtc_trk_lat,  cb_cdata, "/Activities/Activity/Lap/Track/Trackpoint/Position/LatitudeDegrees" },
+    { &GtrnctrFormat::gtc_trk_long, cb_cdata, "/Activities/Activity/Lap/Track/Trackpoint/Position/LongitudeDegrees" },
+    { &GtrnctrFormat::gtc_trk_alt,  cb_cdata, "/Activities/Activity/Lap/Track/Trackpoint/AltitudeMeters" },
+    { &GtrnctrFormat::gtc_trk_dist, cb_cdata, "/Activities/Activity/Lap/Track/Trackpoint/DistanceMeters" },
+    { &GtrnctrFormat::gtc_trk_hr,   cb_cdata, "/Activities/Activity/Lap/Track/Trackpoint/HeartRateBpm" },
+    { &GtrnctrFormat::gtc_trk_cad,  cb_cdata, "/Activities/Activity/Lap/Track/Trackpoint/Cadence" },
+    { &GtrnctrFormat::gtc_trk_pwr,  cb_cdata, "/Activities/Activity/Lap/Track/Trackpoint/Extensions/ns3:TPX/ns3:Watts" },
+    // Sample from Marcelo Kittlein 5/2014 declares a default namespace with the start tag of the TPX element,
+    // and thus doesn't use prefixes.
+    { &GtrnctrFormat::gtc_trk_pwr,  cb_cdata, "/Activities/Activity/Lap/Track/Trackpoint/Extensions/TPX/Watts" },
+    // It looks like Speed and Watts should be siblings, but Garmin can't get
+    // their namespace act very consistent.  This works for a sample provided
+    // by Laurent Desmons in 5/2013.
+    { &GtrnctrFormat::gtc_trk_spd,  cb_cdata, "/Activities/Activity/Lap/Track/Trackpoint/Extensions/TPX/Speed" },
+
+    /* history tcx v1 */
+    { &GtrnctrFormat::gtc_trk_s,    cb_start, "/History/Run" },
+    { &GtrnctrFormat::gtc_trk_ident,cb_cdata, "/History/Run/Id" },
+    { &GtrnctrFormat::gtc_trk_lap_s,cb_start, "/History/Run/Lap" },
+    { &GtrnctrFormat::gtc_trk_lap_e,cb_end,   "/History/Run/Lap" },
+    { &GtrnctrFormat::gtc_trk_pnt_s,cb_start, "/History/Run/Lap/Track/Trackpoint" },
+    { &GtrnctrFormat::gtc_trk_pnt_e,cb_end,   "/History/Run/Lap/Track/Trackpoint" },
+    { &GtrnctrFormat::gtc_trk_utc,  cb_cdata, "/History/Run/Lap/Track/Trackpoint/Time" },
+    { &GtrnctrFormat::gtc_trk_lat,  cb_cdata, "/History/Run/Lap/Track/Trackpoint/Position/LatitudeDegrees" },
+    { &GtrnctrFormat::gtc_trk_long, cb_cdata, "/History/Run/Lap/Track/Trackpoint/Position/LongitudeDegrees" },
+    { &GtrnctrFormat::gtc_trk_alt,  cb_cdata, "/History/Run/Lap/Track/Trackpoint/AltitudeMeters" },
+    { &GtrnctrFormat::gtc_trk_hr,   cb_cdata, "/History/Run/Lap/Track/Trackpoint/HeartRateBpm" },
+    { &GtrnctrFormat::gtc_trk_cad,  cb_cdata, "/History/Run/Lap/Track/Trackpoint/Cadence" },
+
+    { &GtrnctrFormat::gtc_wpt_pnt_s,cb_start, "/Courses/Course/Lap/BeginPosition" },
+    { &GtrnctrFormat::gtc_wpt_pnt_e,cb_end, "/Courses/Course/Lap/BeginPosition" },
+    { &GtrnctrFormat::gtc_wpt_lat,  cb_cdata, "/Courses/Course/Lap/BeginPosition/LatitudeDegrees" },
+    { &GtrnctrFormat::gtc_wpt_long, cb_cdata, "/Courses/Course/Lap/BeginPosition/LongitudeDegrees" },
+    { &GtrnctrFormat::gtc_trk_alt,  cb_cdata, "/Courses/Course/Lap/BeginAltitudeMeters" }
+  };
+
+  int gtc_indent_level{};
+};
+#endif // GTRNCTR_H_INCLUDED_
diff --git a/vecs.h b/vecs.h
index cc78d25bc4230c0a8bff0823d4b7b1a7b286cc90..6afa0e04c30da7e34af7fcdcfd0c95f58510efd2 100644 (file)
--- a/vecs.h
+++ b/vecs.h
@@ -37,6 +37,7 @@
 #include "ggv_bin.h"
 #include "globalsat_sport.h"
 #include "gpx.h"
+#include "gtrnctr.h"
 #include "html.h"
 #include "kml.h"
 #include "legacyformat.h"
@@ -95,7 +96,6 @@ extern ff_vecs_t gpssim_vecs;
 #if CSVFMTS_ENABLED
 extern ff_vecs_t garmin_txt_vecs;
 #endif // CSVFMTS_ENABLED
-extern ff_vecs_t gtc_vecs;
 extern ff_vecs_t dmtlog_vecs;
 extern ff_vecs_t raymarine_vecs;
 extern ff_vecs_t ggv_log_vecs;
@@ -278,7 +278,7 @@ private:
 #if CSVFMTS_ENABLED
   LegacyFormat garmin_txt_fmt {garmin_txt_vecs};
 #endif // CSVFMTS_ENABLED
-  LegacyFormat gtc_fmt {gtc_vecs};
+  GtrnctrFormat gtc_fmt;
   LegacyFormat dmtlog_fmt {dmtlog_vecs};
   LegacyFormat raymarine_fmt {raymarine_vecs};
   LegacyFormat ggv_log_fmt {ggv_log_vecs};